package helper

import (
	"fmt"
	"strings"

	"mall/service/wpgql/api/internal/graph/model"
	"mall/service/wpgql/api/internal/graph/util"
)

func GetMenuToMenuItemConnectionSorterMap(
	pagination *util.ConnectionPagination,
) map[string]string {
	sorterMap := make(map[string]string)

	limit, cursor, reverse := util.GetPageInfo(pagination)

	offset := util.GetOffsetFromCursor(cursor)

	sorterMap["offset"]  = fmt.Sprintf("%v", offset)
	sorterMap["limit"]   = fmt.Sprintf("%v", limit)
	sorterMap["reverse"] = fmt.Sprintf("%v", reverse)

	return sorterMap
}

func MenuToMenuItemConnectionEdgeConverter(
	offset int,
) func(*model.MenuItem, int) *model.MenuToMenuItemConnectionEdge {
	return func(m *model.MenuItem, i int) *model.MenuToMenuItemConnectionEdge {
		return &model.MenuToMenuItemConnectionEdge{
			Cursor: util.ToOffsetCursor(offset + i),
			Node:   m,
		}
	}
}

func GetMenuToMenuItemStartEndCursor(edges []*model.MenuToMenuItemConnectionEdge) (*string, *string) {
	var startCursor *string
	var endCursor *string
	if len(edges) >= 2 {
		startCursor = edges[0].Cursor
		endCursor = edges[len(edges)-1].Cursor
	} else if len(edges) == 1 {
		c := edges[0].Cursor
		startCursor = c
		endCursor = c
	}

	return startCursor, endCursor
}

func GetBlogMenuToMenuItemConnection(
	items []*model.MenuItem,
	hasNextPage bool,
	hasPreviousPage bool,
	offset int32,
	reverse bool,
) *model.MenuToMenuItemConnection {
	if offset >= 0 {
		edges := make([]*model.MenuToMenuItemConnectionEdge, 0, len(items))
		edgeConverter := MenuToMenuItemConnectionEdgeConverter(int(offset))
		util.BaseConnection(len(items), reverse, func(i int) {
			edges = append(edges, edgeConverter(items[i], i))
		})
		startCursor, endCursor := GetMenuToMenuItemStartEndCursor(edges)
		return &model.MenuToMenuItemConnection{
			Edges:    edges,
			Nodes:    items,
			PageInfo: &model.WPPageInfo{
				EndCursor:       endCursor,
				HasNextPage:     hasNextPage,
				HasPreviousPage: hasPreviousPage,
				StartCursor:     startCursor,
			},
		}
	}

	return &model.MenuToMenuItemConnection{
		Nodes: items,
	}
}

func BookToCategoryConnectionEdgeConverter(
	offset int,
) func(*model.Category, int) *model.BookToCategoryConnectionEdge {
	return func(m *model.Category, i int) *model.BookToCategoryConnectionEdge {
		return &model.BookToCategoryConnectionEdge{
			Cursor: util.ToOffsetCursor(offset + i),
			Node:   m,
		}
	}
}

func GetBookToCategoryStartEndCursor(edges []*model.BookToCategoryConnectionEdge) (*string, *string) {
	var startCursor *string
	var endCursor *string
	if len(edges) >= 2 {
		startCursor = edges[0].Cursor
		endCursor = edges[len(edges)-1].Cursor
	} else if len(edges) == 1 {
		c := edges[0].Cursor
		startCursor = c
		endCursor = c
	}

	return startCursor, endCursor
}

func GetBookToCategoryConnection(
	categories []*model.Category,
	hasNextPage bool,
	hasPreviousPage bool,
	offset int32,
	reverse bool,
) *model.BookToCategoryConnection {
	if offset >= 0 {
		edges := make([]*model.BookToCategoryConnectionEdge, 0, len(categories))
		edgeConverter := BookToCategoryConnectionEdgeConverter(int(offset))
		util.BaseConnection(len(categories), reverse, func(i int) {
			edges = append(edges, edgeConverter(categories[i], i))
		})
		startCursor, endCursor := GetBookToCategoryStartEndCursor(edges)
		return &model.BookToCategoryConnection{
			Edges:    edges,
			Nodes:    categories,
			PageInfo: &model.WPPageInfo{
				EndCursor:       endCursor,
				HasNextPage:     hasNextPage,
				HasPreviousPage: hasPreviousPage,
				StartCursor:     startCursor,
			},
		}
	}

	return &model.BookToCategoryConnection{
		Nodes: categories,
	}
}

func BookToSectionConnectionEdgeConverter(
	offset int,
) func(*model.Section, int) *model.BookToSectionConnectionEdge {
	return func(m *model.Section, i int) *model.BookToSectionConnectionEdge {
		return &model.BookToSectionConnectionEdge{
			Cursor: util.ToOffsetCursor(offset + i),
			Node:   m,
		}
	}
}

func GetBookToSectionStartEndCursor(edges []*model.BookToSectionConnectionEdge) (*string, *string) {
	var startCursor *string
	var endCursor *string
	if len(edges) >= 2 {
		startCursor = edges[0].Cursor
		endCursor = edges[len(edges)-1].Cursor
	} else if len(edges) == 1 {
		c := edges[0].Cursor
		startCursor = c
		endCursor = c
	}

	return startCursor, endCursor
}

func GetBookToSectionConnection(
	sections []*model.Section,
	hasNextPage bool,
	hasPreviousPage bool,
	offset int32,
	reverse bool,
) *model.BookToSectionConnection {
	if offset >= 0 {
		edges := make([]*model.BookToSectionConnectionEdge, 0, len(sections))
		edgeConverter := BookToSectionConnectionEdgeConverter(int(offset))
		util.BaseConnection(len(sections), reverse, func(i int) {
			edges = append(edges, edgeConverter(sections[i], i))
		})
		startCursor, endCursor := GetBookToSectionStartEndCursor(edges)
		return &model.BookToSectionConnection{
			Edges:    edges,
			Nodes:    sections,
			PageInfo: &model.WPPageInfo{
				EndCursor:       endCursor,
				HasNextPage:     hasNextPage,
				HasPreviousPage: hasPreviousPage,
				StartCursor:     startCursor,
			},
		}
	}

	return &model.BookToSectionConnection{
		Nodes: sections,
	}
}

func BookToRelationConnectionEdgeConverter(
	offset int,
) func(*model.Book, int) *model.BookToRelationConnectionEdge {
	return func(m *model.Book, i int) *model.BookToRelationConnectionEdge {
		return &model.BookToRelationConnectionEdge{
			Cursor: util.ToOffsetCursor(offset + i),
			Node:   m,
		}
	}
}

func GetBookToRelationStartEndCursor(edges []*model.BookToRelationConnectionEdge) (*string, *string) {
	var startCursor *string
	var endCursor *string
	if len(edges) >= 2 {
		startCursor = edges[0].Cursor
		endCursor = edges[len(edges)-1].Cursor
	} else if len(edges) == 1 {
		c := edges[0].Cursor
		startCursor = c
		endCursor = c
	}

	return startCursor, endCursor
}

func GetBookToRelationConnection(
	relations []*model.Book,
	hasNextPage bool,
	hasPreviousPage bool,
	offset int32,
	reverse bool,
) *model.BookToRelationConnection {
	if offset >= 0 {
		edges := make([]*model.BookToRelationConnectionEdge, 0, len(relations))
		edgeConverter := BookToRelationConnectionEdgeConverter(int(offset))
		util.BaseConnection(len(relations), reverse, func(i int) {
			edges = append(edges, edgeConverter(relations[i], i))
		})
		startCursor, endCursor := GetBookToRelationStartEndCursor(edges)
		return &model.BookToRelationConnection{
			Edges:    edges,
			Nodes:    relations,
			PageInfo: &model.WPPageInfo{
				EndCursor:       endCursor,
				HasNextPage:     hasNextPage,
				HasPreviousPage: hasPreviousPage,
				StartCursor:     startCursor,
			},
		}
	}

	return &model.BookToRelationConnection{
		Nodes: relations,
	}
}

func BookToTagConnectionEdgeConverter(
	offset int,
) func(*model.Tag, int) *model.BookToTagConnectionEdge {
	return func(m *model.Tag, i int) *model.BookToTagConnectionEdge {
		return &model.BookToTagConnectionEdge{
			Cursor: util.ToOffsetCursor(offset + i),
			Node:   m,
		}
	}
}

func GetBookToTagStartEndCursor(edges []*model.BookToTagConnectionEdge) (*string, *string) {
	var startCursor *string
	var endCursor *string
	if len(edges) >= 2 {
		startCursor = edges[0].Cursor
		endCursor = edges[len(edges)-1].Cursor
	} else if len(edges) == 1 {
		c := edges[0].Cursor
		startCursor = c
		endCursor = c
	}

	return startCursor, endCursor
}

func GetBookToTagConnection(
	tags []*model.Tag,
	hasNextPage bool,
	hasPreviousPage bool,
	offset int32,
	reverse bool,
) *model.BookToTagConnection {
	if offset >= 0 {
		edges := make([]*model.BookToTagConnectionEdge, 0, len(tags))
		edgeConverter := BookToTagConnectionEdgeConverter(int(offset))
		util.BaseConnection(len(tags), reverse, func(i int) {
			edges = append(edges, edgeConverter(tags[i], i))
		})
		startCursor, endCursor := GetBookToTagStartEndCursor(edges)
		return &model.BookToTagConnection{
			Edges:    edges,
			Nodes:    tags,
			PageInfo: &model.WPPageInfo{
				EndCursor:       endCursor,
				HasNextPage:     hasNextPage,
				HasPreviousPage: hasPreviousPage,
				StartCursor:     startCursor,
			},
		}
	}

	return &model.BookToTagConnection{
		Nodes: tags,
	}
}

func GetBlogPostConnectionSorterDirection(sorter []*model.PostObjectsConnectionOrderbyInput) util.SortDirection {
	if sorter != nil && len(sorter) > 0 {
		for _, s := range sorter {
			return util.SortDirection(s.Order)
		}
	}

	return util.SortDirectionAsc
}

func GetBlogPostConnectionOrderBy(
	sorter []*model.PostObjectsConnectionOrderbyInput,
	reverse bool,
	defaultDirection util.SortDirection,
) string {
	var a []string
	entityMap, hasEntityMap := util.RpcSorterFieldMap[util.RpcEntityNames.BlogPage]
	if hasEntityMap {
		var handledID bool
		for _, s := range sorter {
			if s.Field == model.PostObjectsConnectionOrderbyEnumIn {
				handledID = true
			}
			column, hasColumn := entityMap[string(s.Field)]
			if hasColumn {
				a = append(a, util.GetOrderBy(
					column,
					util.GetDirection(util.SortDirection(s.Order), reverse),
				))
			}
		}

		if !handledID {
			a = append(a, util.GetOrderBy(
				"Id",
				util.GetDirection(defaultDirection, reverse),
			))
		}
	}

	return strings.Join(a, orderBySeparator)
}

func GetBlogPostConnectionSorterMap(
	pagination *util.ConnectionPagination,
	sorter []*model.PostObjectsConnectionOrderbyInput,
) map[string]string {
	if sorter == nil || len(sorter) == 0 {
		return nil
	}

	sorterMap := make(map[string]string)

	limit, cursor, reverse := util.GetPageInfo(pagination)

	offset := util.GetOffsetFromCursor(cursor)

	direction := GetBlogPostConnectionSorterDirection(sorter)
	orderBy := GetBlogPostConnectionOrderBy(sorter, reverse, direction)

	sorterMap["offset"]  = fmt.Sprintf("%v", offset)
	sorterMap["limit"]   = fmt.Sprintf("%v", limit)
	sorterMap["reverse"] = fmt.Sprintf("%v", reverse)
	sorterMap["sorter"]  = fmt.Sprintf("%v", orderBy)

	return sorterMap
}

func PostToCategoryConnectionEdgeConverter(
	offset int,
) func(*model.Category, int) *model.PostToCategoryConnectionEdge {
	return func(m *model.Category, i int) *model.PostToCategoryConnectionEdge {
		return &model.PostToCategoryConnectionEdge{
			Cursor: util.ToOffsetCursor(offset + i),
			Node:   m,
		}
	}
}

func GetPostToCategoryStartEndCursor(edges []*model.PostToCategoryConnectionEdge) (*string, *string) {
	var startCursor *string
	var endCursor *string
	if len(edges) >= 2 {
		startCursor = edges[0].Cursor
		endCursor = edges[len(edges)-1].Cursor
	} else if len(edges) == 1 {
		c := edges[0].Cursor
		startCursor = c
		endCursor = c
	}

	return startCursor, endCursor
}

func GetPostToCategoryConnection(
	categories []*model.Category,
	hasNextPage bool,
	hasPreviousPage bool,
	offset int32,
	reverse bool,
) *model.PostToCategoryConnection {
	if offset >= 0 {
		edges := make([]*model.PostToCategoryConnectionEdge, 0, len(categories))
		edgeConverter := PostToCategoryConnectionEdgeConverter(int(offset))
		util.BaseConnection(len(categories), reverse, func(i int) {
			edges = append(edges, edgeConverter(categories[i], i))
		})
		startCursor, endCursor := GetPostToCategoryStartEndCursor(edges)
		return &model.PostToCategoryConnection{
			Edges:    edges,
			Nodes:    categories,
			PageInfo: &model.WPPageInfo{
				EndCursor:       endCursor,
				HasNextPage:     hasNextPage,
				HasPreviousPage: hasPreviousPage,
				StartCursor:     startCursor,
			},
		}
	}

	return &model.PostToCategoryConnection{
		Nodes: categories,
	}
}

func PostToTagConnectionEdgeConverter(
	offset int,
) func(*model.Tag, int) *model.PostToTagConnectionEdge {
	return func(m *model.Tag, i int) *model.PostToTagConnectionEdge {
		return &model.PostToTagConnectionEdge{
			Cursor: util.ToOffsetCursor(offset + i),
			Node:   m,
		}
	}
}

func GetPostToTagStartEndCursor(edges []*model.PostToTagConnectionEdge) (*string, *string) {
	var startCursor *string
	var endCursor *string
	if len(edges) >= 2 {
		startCursor = edges[0].Cursor
		endCursor = edges[len(edges)-1].Cursor
	} else if len(edges) == 1 {
		c := edges[0].Cursor
		startCursor = c
		endCursor = c
	}

	return startCursor, endCursor
}

func GetPostToTagConnection(
	tags []*model.Tag,
	hasNextPage bool,
	hasPreviousPage bool,
	offset int32,
	reverse bool,
) *model.PostToTagConnection {
	if offset >= 0 {
		edges := make([]*model.PostToTagConnectionEdge, 0, len(tags))
		edgeConverter := PostToTagConnectionEdgeConverter(int(offset))
		util.BaseConnection(len(tags), reverse, func(i int) {
			edges = append(edges, edgeConverter(tags[i], i))
		})
		startCursor, endCursor := GetPostToTagStartEndCursor(edges)
		return &model.PostToTagConnection{
			Edges:    edges,
			Nodes:    tags,
			PageInfo: &model.WPPageInfo{
				EndCursor:       endCursor,
				HasNextPage:     hasNextPage,
				HasPreviousPage: hasPreviousPage,
				StartCursor:     startCursor,
			},
		}
	}

	return &model.PostToTagConnection{
		Nodes: tags,
	}
}

func RootQueryToPostConnectionEdgeConverter(
	offset int,
) func(*model.Post, int) *model.RootQueryToPostConnectionEdge {
	return func(m *model.Post, i int) *model.RootQueryToPostConnectionEdge {
		return &model.RootQueryToPostConnectionEdge{
			Cursor: util.ToOffsetCursor(offset + i),
			Node:   m,
		}
	}
}

func GetRootQueryToPostStartEndCursor(edges []*model.RootQueryToPostConnectionEdge) (*string, *string) {
	var startCursor *string
	var endCursor *string
	if len(edges) >= 2 {
		startCursor = edges[0].Cursor
		endCursor = edges[len(edges)-1].Cursor
	} else if len(edges) == 1 {
		c := edges[0].Cursor
		startCursor = c
		endCursor = c
	}

	return startCursor, endCursor
}

func GetRootQueryToPostConnection(
	entries []*model.Post,
	hasNextPage bool,
	hasPreviousPage bool,
	offset int32,
	reverse bool,
) *model.RootQueryToPostConnection {
	if offset >= 0 {
		edges := make([]*model.RootQueryToPostConnectionEdge, 0, len(entries))
		edgeConverter := RootQueryToPostConnectionEdgeConverter(int(offset))
		util.BaseConnection(len(entries), reverse, func(i int) {
			edges = append(edges, edgeConverter(entries[i], i))
		})
		startCursor, endCursor := GetRootQueryToPostStartEndCursor(edges)
		return &model.RootQueryToPostConnection{
			Edges:    edges,
			Nodes: entries,
			PageInfo: &model.WPPageInfo{
				EndCursor:       endCursor,
				HasNextPage:     hasNextPage,
				HasPreviousPage: hasPreviousPage,
				StartCursor:     startCursor,
			},
		}
	}

	return &model.RootQueryToPostConnection{
		Nodes: entries,
	}
}

func RootQueryToPageConnectionEdgeConverter(
	offset int,
) func(*model.Page, int) *model.RootQueryToPageConnectionEdge {
	return func(m *model.Page, i int) *model.RootQueryToPageConnectionEdge {
		return &model.RootQueryToPageConnectionEdge{
			Cursor: util.ToOffsetCursor(offset + i),
			Node:   m,
		}
	}
}

func GetRootQueryToPageStartEndCursor(edges []*model.RootQueryToPageConnectionEdge) (*string, *string) {
	var startCursor *string
	var endCursor *string
	if len(edges) >= 2 {
		startCursor = edges[0].Cursor
		endCursor = edges[len(edges)-1].Cursor
	} else if len(edges) == 1 {
		c := edges[0].Cursor
		startCursor = c
		endCursor = c
	}

	return startCursor, endCursor
}

func GetRootQueryToPageConnection(
	pages []*model.Page,
	hasNextPage bool,
	hasPreviousPage bool,
	offset int32,
	reverse bool,
) *model.RootQueryToPageConnection {
	if offset >= 0 {
		edges := make([]*model.RootQueryToPageConnectionEdge, 0, len(pages))
		edgeConverter := RootQueryToPageConnectionEdgeConverter(int(offset))
		util.BaseConnection(len(pages), reverse, func(i int) {
			edges = append(edges, edgeConverter(pages[i], i))
		})
		startCursor, endCursor := GetRootQueryToPageStartEndCursor(edges)
		return &model.RootQueryToPageConnection{
			Edges:    edges,
			Nodes:    pages,
			PageInfo: &model.WPPageInfo{
				EndCursor:       endCursor,
				HasNextPage:     hasNextPage,
				HasPreviousPage: hasPreviousPage,
				StartCursor:     startCursor,
			},
		}
	}
	
	return &model.RootQueryToPageConnection{
		Nodes: pages,
	}
}

func RootQueryToBookConnectionEdgeConverter(
	offset int,
) func(*model.Book, int) *model.RootQueryToBookConnectionEdge {
	return func(m *model.Book, i int) *model.RootQueryToBookConnectionEdge {
		return &model.RootQueryToBookConnectionEdge{
			Cursor: util.ToOffsetCursor(offset + i),
			Node:   m,
		}
	}
}

func GetRootQueryToBookStartEndCursor(edges []*model.RootQueryToBookConnectionEdge) (*string, *string) {
	var startCursor *string
	var endCursor *string
	if len(edges) >= 2 {
		startCursor = edges[0].Cursor
		endCursor = edges[len(edges)-1].Cursor
	} else if len(edges) == 1 {
		c := edges[0].Cursor
		startCursor = c
		endCursor = c
	}

	return startCursor, endCursor
}

func GetRootQueryToBookConnection(
	entries []*model.Book,
	hasNextPage bool,
	hasPreviousPage bool,
	offset int32,
	reverse bool,
) *model.RootQueryToBookConnection {
	if offset >= 0 {
		edges := make([]*model.RootQueryToBookConnectionEdge, 0, len(entries))
		edgeConverter := RootQueryToBookConnectionEdgeConverter(int(offset))
		util.BaseConnection(len(entries), reverse, func(i int) {
			edges = append(edges, edgeConverter(entries[i], i))
		})
		startCursor, endCursor := GetRootQueryToBookStartEndCursor(edges)
		return &model.RootQueryToBookConnection{
			Edges:    edges,
			Nodes:    entries,
			PageInfo: &model.WPPageInfo{
				EndCursor:       endCursor,
				HasNextPage:     hasNextPage,
				HasPreviousPage: hasPreviousPage,
				StartCursor:     startCursor,
			},
		}
	}

	return &model.RootQueryToBookConnection{
		Nodes: entries,
	}
}
